/*
 * Copyright (c) 2008, Solido Systems
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of Solido Systems nor the names of its contributors may be
 * used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.solidosystems.objectkitchen.data;

import java.io.*;

public abstract class Value{
	public final static int STRING		= 0;
	public final static int INTEGER		= 1;
	public final static int LONG		= 2;
	public final static int FLOAT		= 3;
	public final static int DOUBLE		= 4;
	public final static int BOOLEAN		= 5;
	public final static int BYTEARRAY	= 6;
	public final static int LIST		= 7;
	public final static int HASH		= 8;
	
	public abstract String getTypeName();
	public abstract int getTypeId();
	public abstract String toString();
	public abstract Integer toInteger();
	public abstract Long toLong();
	public abstract Float toFloat();
	public abstract Double toDouble();
	public abstract Boolean toBoolean();
	public abstract byte[] toByteArray();
	public abstract boolean equalsSameType(Value val);
	
	public static Value create(String str){
		return new VString(str);
	}
	public static Value create(int i){
		return new VInteger(i);
	}
	public static Value create(long l){
		return new VLong(l);
	}
	public static Value create(float f){
		return new VFloat(f);
	}
	public static Value create(double d){
		return new VDouble(d);
	}
	public static Value create(boolean b){
		return new VBoolean(b);
	}
	public static Value create(byte[] data){
		return new VByteArray(data);
	}
	
	/**
	 * Performs lookups into a structure of lists and hashes using the following notation: foo.bar[7].baz
	 */
	public Value locate(String query){
		// We could speed this significantly up with integer based types!
		try{
			if(query.startsWith("."))query=query.substring(1);
			if(query.length()==0)return this;
			if(query.startsWith("[")){
				query=query.substring(1);
				if(getTypeName().equals("List")){
					VList lst=(VList)this;
					if(query.indexOf("]")>-1){
						String index=query.substring(0,query.indexOf("]"));
						query=query.substring(query.indexOf("]")+1);
						int i=Integer.parseInt(index);
						if(i<lst.size()){
							Value val=lst.get(i);
							if(query.length()==0){
								return val;
							}else{
								return val.locate(query);
							}
						}
					}
				}
				return null;
			}
			if(getTypeName().equals("Hash")){
				String key=query;
				if(key.indexOf(".")>-1){
					if(key.indexOf("[")>-1){
						if(key.indexOf(".")<key.indexOf("[")){
							key=key.substring(0,key.indexOf("."));
							query=query.substring(query.indexOf(".")+1);
						}else{
							key=key.substring(0,key.indexOf("["));
							query=query.substring(query.indexOf("["));
						}
					}else{
						key=key.substring(0,key.indexOf("."));
						query=query.substring(query.indexOf(".")+1);
					}
				}else if(key.indexOf("[")>-1){
					key=key.substring(0,key.indexOf("["));
					query=query.substring(query.indexOf("["));
				}else{
					query="";
				}
				VHash hsh=(VHash)this;
				if(hsh.containsKey(key)){
					Value val=hsh.get(key);
					if(query.length()==0){
						return val;
					}else{
						return val.locate(query);
					}
				}
			}
		}catch(Exception e){
			// Should be removed after the method is fully debugged
			e.printStackTrace();
		}
		return null;
	}
	
	public abstract void writeToStream(DataOutputStream stream) throws IOException;
	
	public static Value readFromStream(DataInputStream stream) throws IOException{
		int type=stream.readByte();
		switch(type){
			case STRING		: return VString.readDataFromStream(stream);
			case INTEGER	: return VInteger.readDataFromStream(stream);
			case LONG		: return VLong.readDataFromStream(stream);
			case FLOAT		: return VFloat.readDataFromStream(stream);
			case DOUBLE		: return VDouble.readDataFromStream(stream);
			case BOOLEAN	: return VBoolean.readDataFromStream(stream);
			case BYTEARRAY	: return VByteArray.readDataFromStream(stream);
			case LIST		: return VList.readDataFromStream(stream);
			case HASH		: return VHash.readDataFromStream(stream);
		}
		return null;
	}
	
	public boolean equals(Object obj){
		try{
			Value val=(Value)obj;
			if(val.getTypeId()==getTypeId()){
				return equalsSameType(val);
			}
			// in the future we should check for comparable values such as long and int
		}catch(Exception e){}
		return false;
	}
}